URP Render Graph自定义Pass
全库目录221
本文目录10
一个全屏材质放进URP后,看不到通道,可能是没有入队,也可能是输出没有后续读者而被图剔除。RecordRenderGraph先声明资源和回调,实际绘制在执行阶段发生。我们从相机颜色到新目标,再把输出交回相机资源,沿这条路径检查每一步。
适用环境
接口范围为 Unity 6.0 / URP 17 的 Render Graph 路径,核对日期 2026-10-22。以下代码为自定义 ScriptableRenderPass 内的方法片段,使用官方记录模式;类壳、材质资产和 feature 入队代码由项目提供。完整Unity工程编译与运行尚未验证。
记录、执行与输出交接
Renderer Feature.Create:建立材质和通道
→ AddRenderPasses:按相机条件入队
→ RecordRenderGraph:声明输入、输出和回调
→ 图编译:剔除、分配、同步与合并
→ 回调:读取 PassData,记录实际绘制Feature 的 Create 可能因编辑器重建多次调用,需处理已有资源。长期对象在 Dispose 时释放;图内纹理由图管理。旧式 Execute 路径不能直接当成渲染图回调使用。
Feature与相机入口
Unity 6.0 / URP17的Feature在Create建立Pass,在AddRenderPasses按相机类型入队。图路径把资源声明放在RecordRenderGraph,避免在Feature长期保存帧内TextureHandle。兼容路径的SetupRenderPasses与RTHandle访问按目标包接口核对。
下面是Feature类中的C#方法片段,m_pass由Create初始化,输出是本相机Pass队列。它不包含完整类、资源释放或图声明,完整Unity编译运行尚未验证。
/// <summary>Creates the pass owned by this feature.</summary>
public override void Create()
{
m_pass = new ColorEffectPass();
}
/// <summary>Queues the pass for game cameras.</summary>
public override void AddRenderPasses(
ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (renderingData.cameraData.cameraType != CameraType.Game)
{
return;
}
renderer.EnqueuePass(m_pass);
}CameraType.Game筛选只回答是否为游戏相机,不能排除Overlay。若Base和Overlay都入队并各做一次强度0.5的颜色乘法,共享最终画面可能被处理成0.25;效果参数正确也会显得过暗。先明确设计为每个相机运行,还是仅在选定堆栈阶段运行,再按包版本提供的相机角色或最终解析标记筛选。记录相机ID、角色和输出颜色版本,才能把重复处理与材质公式错误分开。XR与动态分辨率由图资源描述接入。
最小材质 Blit
输入是当前相机颜色,输出是图内的新颜色纹理。输出句柄交回相机资源,使后续通道消费它。所选注入点需位于可采样颜色可用的阶段。
public override void RecordRenderGraph(RenderGraph graph, ContextContainer frameData)
{
UniversalResourceData resources = frameData.Get<UniversalResourceData>();
if (resources.isActiveTargetBackBuffer)
{
return;
}
TextureHandle source = resources.activeColorTexture;
TextureDesc descriptor = graph.GetTextureDesc(source);
descriptor.name = "CustomColor";
descriptor.clearBuffer = false;
TextureHandle destination = graph.CreateTexture(descriptor);
using (IRasterRenderGraphBuilder builder =
graph.AddRasterRenderPass<PassData>("CustomColor", out PassData data))
{
data.source = source;
data.material = m_material;
builder.UseTexture(source, AccessFlags.Read);
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
builder.SetRenderFunc((PassData passData, RasterGraphContext context) =>
{
Blitter.BlitTexture(context.cmd, passData.source,
new Vector4(1, 1, 0, 0), passData.material, 0);
});
}
resources.cameraColor = destination;
}PassData 包含 TextureHandle source 与 Material material,m_material 是该通道已创建并持有的材质字段。方法省略命名空间,主要来自 Rendering、RenderGraphModule 与 Universal。材质需兼容 URP Blitter 和 XR 采样路径。完整覆盖才允许不清除目标,片元丢弃或局部 viewport 时应处理未写区域。
颜色格式与样本数继承输入,MSAA 输入是否可以直接采样需按资源状态和算法确认。回调中的缩放向量示例适用于完整区域;RTHandle 缩放、XR 和动态尺寸用官方 Blitter 约定核对。
资源描述与最小复现条件
先在单相机、非XR、关闭MSAA和动态分辨率的Unity 6000.0 / URP17项目中接入。选择BeforeRenderingPostProcessing作为测试事件,给通道声明颜色需求,确保isActiveTargetBackBuffer为false;若仍为true,检查Renderer的中间纹理配置。返回分支只是防止错误采样,它也意味着本帧没有运行效果,诊断日志必须能区分这两种情况。
TextureDesc复制输入的尺寸和格式,但普通Texture2D采样示例只适用于单采样源。开启MSAA时先确认输入已经resolve,再建立单采样目标;直接把多采样附件当作普通采样纹理会违反数据类型约定。XR增加数组切片,动态分辨率增加有效viewport与分配尺寸的差异,扩展时按所用包的Blitter采样宏与缩放规则分别验证。
图内句柄表示这一帧的逻辑资源。相机颜色原先指向A,通道读取A、完整写B,然后把cameraColor设为B,后续消费者才沿B建立依赖。A与B是两份资源,输出交接不要求把B再复制回A。输出交接方式在Blit文档的Avoid blitting back节有明确示例;2026-10-22核读。
验收时先用恒等材质让B等于A,再去掉cameraColor交接观察剔除,最后恢复交接并加入效果。这样分别验证采样、图依赖和材质算法,而不会把三类问题混在一张黑图中。
常见错误
只创建和写出纹理,却没有后续读者或相机资源交接,通道可能被剔除。诊断时可以暂时禁用剔除,正式路径应建立消费者。
把同一颜色同时作为采样源和附件写入可能构成反馈回路。通常分配不同输出或采用明确支持的 local read 路径。外部全局纹理发布还涉及依赖跟踪,优先使用明确的传递和引擎支持的全局声明接口。
材质参数在记录后、执行前被其他相机修改,会让回调读到不符合该相机的状态。逐视图数据保存在 PassData 或受控参数缓冲中。
Frame Debugger 与图查看器
Frame Debugger 展示绘制、附件和结果,图查看器展示逻辑通道、资源寿命及合并。看不到通道时先确认相机筛选、入队、消费者和剔除。合并可能改变捕获中看到的 API 通道边界,应结合图名称定位。
验证方法
- 用纯色材质确认注入位置,再换成采样原输入的材质。
- 故意去掉输出交接,检查通道剔除;恢复后检查资源依赖。
- 添加两个相机与 Overlay,确认每个视图的数据独立。
- 切换 MSAA、render scale 与 XR,查看实际尺寸、切片和 resolve。
- 检查图内新纹理数量与峰值,确认没有每帧常驻资源泄漏。
相关主题
参考资料
- Unity 6 Write a Render Pass。
- Unity 6 Render Graph。
- Unity Graphics 包源码:
ScriptableRenderPass.cs、UniversalResourceData.cs、DrawObjectsPass.cs;复现时锁定 URP 17 源码。